#include "QxWellDatabaseIO.hpp"

#include <QVariant>
#include <QSqlError>
#include <QSqlQuery>
#include <QStringList>

class QxWellDatabaseIO::QxWellDatabaseIOPrivate
{
public:

  QxWellDatabaseIOPrivate( QxWellDatabaseIO* me ) : m_self( me ) {
  }

  void reload( const QSqlDatabase& ) {
  }

  static bool identifyTables( const QSqlDatabase& db ) {
    QStringList tables = db.tables();
    if( !tables.contains( "DB_WELLINFO" ) && !createWellInfoTable( db ) ) {
      return false;
    }

    if( !tables.contains( "DB_WELLLOG" ) && !createWellLogTable( db ) ) {
      return false;
    }

    return true;
  }

  static bool createWellInfoTable( const QSqlDatabase& db ) {
    QSqlQuery query( db );
    return query.exec( "create table DB_WELLINFO (                        "
                       "id             integer primary key autoincrement, "
                       "well_name      varchar(64),                       "
                       "x_coordinate   real,                              "
                       "y_coordinate   real,                              "
                       "start_depth    real,                              "
                       "stop_depth     real,                              "
                       "step_depth     real,                              "
                       "null_value     real                               "
                       ")                                               " );
  }

  static bool createWellLogTable( const QSqlDatabase& db ) {
    QSqlQuery query( db );
    return query.exec( "create table DB_WELLLOG(                           "
                       "id            integer primary key autoincrement,   "
                       "well_name     references DB_WELLINFO( well_name ), "
                       "well_log_name varchar(64),                         "
                       "start_depth   real,                                "
                       "stop_depth    real,                                "
                       "step_depth    real,                                "
                       "null_value    real,                                "
                       "type          int,                                 "
                       "data          blob                                 "
                       ")                                                 " );
    return true;
  }

  QByteArray encrypt( const QVector<float>& data ) {
    QByteArray result;
    QDataStream dataOut( &result, QIODevice::WriteOnly );
    dataOut << data;

    return qCompress( result, 9 );
  }

  QVector<float> decrypt( const QByteArray& data ) {
    QVector<float> result;
    QDataStream dataIn( qUncompress( data ) );
    dataIn >> result;

    return result;
  }

  QxWellDatabaseIO*         m_self;

  QString                   m_wellName;
};

QxWellDatabaseIO::QxWellDatabaseIO( const QSqlDatabase& database )
  : QxAbstractDatabaseIO( database ), _pd( new QxWellDatabaseIOPrivate( this ) )
{
}

QxWellDatabaseIO::~QxWellDatabaseIO()
{
}

const QString& QxWellDatabaseIO::wellName() const
{
  return _pd->m_wellName;
}

void QxWellDatabaseIO::setWellName( const QString& wellName )
{
  _pd->m_wellName = wellName;
}

QxWellInfoDefine QxWellDatabaseIO::wellInfoDefine() const
{
  QxWellInfoDefine result;

  QSqlQuery query( database() );
  query.prepare( "select x_coordinate, y_coordinate, start_depth, stop_depth, step_depth, null_value from DB_WELLINFO where well_name=?" );
  query.addBindValue( _pd->m_wellName );
  if( query.exec() && query.next() ) {
    result.wellName = query.value( 0 ).toString();
    result.location = QPointF(query.value( 1 ).toFloat(), query.value( 2 ).toFloat());
    result.startDepth = query.value( 3 ).toFloat();
    result.stopDepth  = query.value( 4 ).toFloat();
    result.stepDepth  = query.value( 5 ).toFloat();
    result.nullValue  = query.value( 6 ).toFloat();
  }

  return result;
}

void QxWellDatabaseIO::setWellInfoDefine( const QxWellInfoDefine& wellInfoDefine )
{
  QSqlQuery query( database() );
  query.prepare( "update DB_WELLINFO set x_coordinate=?, y_coordinate=?, start_depth=?, stop_depth=?, step_depth=?, null_value=? where well_name=? )" );
  query.addBindValue( wellInfoDefine.location.x() );
  query.addBindValue( wellInfoDefine.location.y() );
  query.addBindValue( wellInfoDefine.startDepth );
  query.addBindValue( wellInfoDefine.stopDepth );
  query.addBindValue( wellInfoDefine.stepDepth );
  query.addBindValue( wellInfoDefine.nullValue );
  query.addBindValue( wellInfoDefine.wellName );
  query.exec();
}

QVector<QxWellLogDefine> QxWellDatabaseIO::wellLogDefines() const
{
  QVector<QxWellLogDefine> result;

  QSqlQuery query( database() );
  query.prepare( "select well_log_name, start_depth, stop_depth, step_depth, null_value type from DB_WELLLOG where well_name=?" );
  query.addBindValue( _pd->m_wellName );
  while( query.next() ) {
    QxWellLogDefine wellLogDefine;
    wellLogDefine.wellName    = _pd->m_wellName;
    wellLogDefine.wellLogName = query.value( 0 ).toString();
    wellLogDefine.startDepth  = query.value( 1 ).toFloat();
    wellLogDefine.stopDepth   = query.value( 2 ).toFloat();
    wellLogDefine.stepDepth   = query.value( 3 ).toFloat();
    wellLogDefine.nullValue   = query.value( 4 ).toFloat();
    wellLogDefine.type        = query.value( 5 ).toInt();
    result << wellLogDefine;
  }

  return result;
}

bool QxWellDatabaseIO::insertWellLogDefine( const QxWellLogDefine& wellLogDefine )
{
  QSqlQuery query( database() );
  query.prepare( "insert into DB_WELLLOG( well_name, well_log_name, start_depth, stop_depth, step_depth, null_value, type ) values( ?, ?, ?, ?, ?, ?, ? )" );
  query.addBindValue( wellLogDefine.wellName );
  query.addBindValue( wellLogDefine.wellLogName );
  query.addBindValue( wellLogDefine.startDepth );
  query.addBindValue( wellLogDefine.stopDepth );
  query.addBindValue( wellLogDefine.stepDepth );
  query.addBindValue( wellLogDefine.nullValue );
  query.addBindValue( wellLogDefine.type );
  return query.exec();
}

bool QxWellDatabaseIO::removeWellLogDefine( const QxWellLogDefine& wellLogDefine )
{
  QSqlQuery query( database() );
  query.prepare( "delete from DB_WELLLOG where well_name=? and well_log_name=?" );
  query.addBindValue( wellLogDefine.wellName );
  query.addBindValue( wellLogDefine.wellLogName );
  return query.exec();
}

QVector<float> QxWellDatabaseIO::data( const QString& wellLogName ) const
{
  QVector<float> result;
  QSqlQuery query( database() );
  query.prepare( QString( "select data from DB_WELLLOG where well_log_name='%1'" ).arg( wellLogName ) );
  if( query.exec() && query.next() ) {
    result = _pd->decrypt( query.value( 0 ).toByteArray() );
  }

  return result;
}

void QxWellDatabaseIO::setData( const QString& wellLogName, const QVector<float>& data )
{
  QSqlQuery query( database() );
  query.prepare( "update DB_WELLLOG set data=? where well_name=? and well_log_name=?" );
  query.bindValue( 0, _pd->encrypt( data ) );
  query.bindValue( 1, _pd->m_wellName );
  query.bindValue( 2, wellLogName );
  query.exec();
}

QStringList QxWellDatabaseIO::queryWellLogNames()
{
  QStringList result;

  QSqlQuery query( database() );
  query.prepare( "select well_log_name from DB_WELLLOG where well_name=?" );
  query.addBindValue( _pd->m_wellName );
  query.exec();
  while( query.next() ) {
    result << query.value( 0 ).toString();
  }

  return result;
}

void QxWellDatabaseIO::clear()
{
  QSqlQuery query( database() );
  query.prepare( "delete from DB_WELLLOG where well_name=?" );
  query.addBindValue( _pd->m_wellName );
  query.exec();
}

void QxWellDatabaseIO::createTable( const QSqlDatabase& db )
{
  QxWellDatabaseIOPrivate::identifyTables( db );
}

void QxWellDatabaseIO::init()
{
  if( _pd->identifyTables( database() ) ) {
    _pd->reload( database() );
  }
}
